<template>
  <!-- 此组件为动态表单组件 -->
  <el-form :inline="true" class="form" ref="form" :model="model" :rules="rules" :label-width="labelComputedWidth + 'px'">
    <el-form-item class="formLine" v-for="(item, i) in cols" :key="'formNever'+ i" :prop="item.prop" :label="item.label+':'" 
    :required="item.required" :style="{width: item.width +'%'}">
      <template v-if="item.type == 'custom'">
        <!-- 自定义插槽 -->
        <!-- 解决部分不需要必填，但是需要红色标识符的情况 -->
        <label slot="label" v-if="item.uploadRequired">
          <span style="color:red;margin-right: .25rem;">*</span>{{item.label}}
        </label>
        <slot :name="item.prop" />
      </template>
      
      <template v-if="item.type == 'text' || item.type == 'textarea' || item.type == 'password'">
        <!-- 输入框 / 文本域 / 密码输入框-->
        <el-input :type="item.type" :rows="String(item.rows || 3)" resize="none" v-model="model[item.prop]" :maxlength="item.max || 40" 
        :title="item.disabled ? model[item.prop]:''"  :disabled="item.disabled"></el-input>
      </template>

      <template v-if="item.type == 'select'">
        <!-- 下拉选择框(后期会集成虚拟列表选择器) -->
        <el-select v-model="model[item.prop]" :title="item.disabled ? model[item.prop]:''" 
        :disabled="item.disabled" @change="item.fn && item.fn(model[item.prop])" :multiple="item.multiple ? true : false">
          <el-option
            v-for="(item, i) in item.select" :key="'item_' + i" :label="item.label || item.text" :value="item.value">
          </el-option>
        </el-select>
      </template>
  
      <template v-if="item.type == 'date' || item.type == 'month' || item.type == 'datetimerange' || item.type == 'year'">
        <!-- 年份/时间/时间区间 选择器 -->
        <el-date-picker 
          v-model="model[item.prop]" :type="item.type" :format="forMat(item.type)" :value-format="forMat(item.type)" 
          :disabled="item.disabled" placeholder="请选择">
        </el-date-picker>
      </template>

      <!-- 整数值的输入框 -->
      <template v-if="item.type == 'number'">
        <el-input v-model.number="model[item.prop]" type="text" :maxlength="item.max || 40" :title="item.disabled ? model[item.prop]:''"  
        :disabled="item.disabled"></el-input>
      </template>

      <!-- 限制任意小数位的数字输入框 -->
      <template v-if="item.type == 'floatNum'">
        <el-input v-toFixedNum:[item.decimal] v-model="model[item.prop]" type="text" :maxlength="item.max || 40" 
        :title="item.disabled ? model[item.prop]:''" :disabled="item.disabled"></el-input>
      </template>

    </el-form-item>
  </el-form>
</template>

<script lang="ts" setup>
import type { cols, trigger } from './public';
import type { PropType } from "vue";

import { defineProps, reactive, defineEmit, ref, watch, useContext } from "vue";
import { ergExtFunc, regExpCustom, blurArrType, forMat, computedLableWidth} from './public';
import type { ElForm } from 'element-plus'

// 导出子组件需要暴露出去的方法
const { expose } = useContext()

// 父子之间通信传值
let emit = defineEmit(['update:model']);

// 当前组件实例
let form = ref<InstanceType<typeof ElForm>>();

let props = defineProps({
  // 绑定的表单对象
  model:{
    type: Object,
    default(){ return {} }
  },
  // 表单配置项
  cols: {
    type: Array as PropType<cols[]>,
    default(){ return [] }
  },
  // 左侧文字显示长度
  labelWidth:{
    type: Number,
    default: 0
  }
})

// 存储动态生成的校验规则
let rules = reactive<any>({})

// 动态计算出来的宽度
let labelComputedWidth = ref(0)

const reloadCols = (data: cols[]) =>{
  rules = {};
  data.forEach((v: cols)=>{
    let ruleArr = [];
    // 必填校验
    if(v.required){
      let arr = [
        { required: true, message: `${v.label}为必填项`,trigger: 'blur'},
        { required: true, message: `${v.label}为必填项`,trigger: 'change'}
      ]
      ruleArr.push(...arr)
    };
    // 自定义校验规则
    if(v.validate){
      // 当自定义规则为字符串时
      if(typeof(v.validate) == 'string'){
        let arr:any = [];
        const validateList = v.validate.split('|');
        validateList.forEach((validate: any) => {
          // 以下为用户添加的自定义校验规则
          if (validate == 'pattern') { arr.push(regExpCustom(v)); }else{
            arr.push(ergExtFunc(validate, v.trigger ? v.trigger : []));
          }
        });
      }
      if(typeof(v.validate) == 'function'){
        // 针对不同类型的配置项生成单独的触发方式
        let arr:any = [];
        if(blurArrType.includes(v.type)){
          arr = [{ validator: v.validate.bind(this, props.model), trigger: 'blur'}]
        }else{
          arr = [{ validator: v.validate.bind(this, props.model), trigger: 'change'}]
        }
        // 有可能两个校验规则都需要触发,或者自定义校验方式的
        if(v.trigger && v.trigger.length){
          v.trigger.forEach((j: trigger)=>{
            typeof(v.validate) == 'function' && arr.push({ validator: v.validate.bind(this, props.model), trigger: j })
          })
        }
        ruleArr.push(...arr)
      }
    }

    // 将校验规则绑定表单元素
    if (ruleArr.length > 0) {
      rules[v.prop]  = ruleArr
    }
  })
}

// 监听配置项的变化生成不同的校验规则
watch(()=> props.cols, (n:cols[])=>{
  // 生成校验规则
  reloadCols(n)
  // 动态计算高度
  labelComputedWidth.value = props.labelWidth ? props.labelWidth : computedLableWidth(n);
},{ deep: true, immediate: true })

// 监听表单绑定对象的变化
watch(()=> props.model, (n:any)=> {
  emit('update:model', n)
},{ deep: true })

// 触发表单的校验规则
const verify = () => form.value?.validate()

// 清空某个校验规则
const clearValidate = (val: string) => form.value?.clearValidate(val)

// 重置整个表单
const resetFields = () => form.value?.resetFields();

expose({
  verify,
  clearValidate,
  resetFields
})

</script>

<style lang="scss" scoped>
.form{
  width: 100%;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: space-between;
  .formLine{
    display: flex;
    align-items: center;
    margin-right: 10px;
  }
}

:deep(.el-input__inner){
  min-width: 220px !important;
  max-width: 220px !important;
}
:deep(.el-form-item__content){
  flex: 1;
  margin-right: 20px;
}
</style>